using DG.Tweening;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
using LogSystem;
using NodeCanvas.Framework;
using OWL.Rendering.HRP;
using Pathfinding;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using Yoozoo.Core.Extensions.Engine;
using Yoozoo.Framework.Core;
using Yoozoo.Gameplay.RTS;
using Yoozoo.Managers.ResourceManagerV2.Runtime;
using Yoozoo.Mars.Got;
using Random = UnityEngine.Random;

namespace Yoozoo.Gameplay.City
{
    public class CityRealAiManager : Singleton<CityRealAiManager>
    {
        private bool _isInitialize = false;
        
        public CityRealAiConfigManager ConfigMgr { get; private set; }
        
        public CityRealAiPoolManager PoolMgr { get; private set; }

        public Transform CityRealAiRootTran { get; private set; }
        
        public CityRealHudManager HudMgr { get; private set; }

        private HcitySunoRainData _dailyData;
        public HcitySunoRainData DailyData
        {
            get
            {
                if (_dailyData == null)
                {
                    _dailyData = HRenderSettingsManager.Get().GetData<HcitySunoRainData>();
                }

                return _dailyData;
            }
        }

        public bool UnlockBus
        {
            get
            {
                return false;   // 强制隐藏巴士
            }
            private set
            {
                _UnlockBus = value;
            }
        }
        private bool _UnlockBus;

        /// <summary>
        /// 城市是否在晚上
        /// </summary>
        public bool CityIsInNight
        {
            get
            {
                if (DailyData == null)
                {
                    return false;
                }

                return Array.IndexOf(CityRealAiConst.GetOffTransportStageArray, SetSunorRain.getInstance().Stage) >= 0;
            }
        }
        
        /// <summary>
        /// key = areaId value = area ai manager
        /// </summary>
        public Dictionary<int, CityRealAiAreaManager> AreaDic { get; private set; }
        
        /// <summary>
        /// 区域里未雇佣的雇员小人
        /// </summary>
        public Dictionary<int, CityAreaNpcController> StaffAreaDic { get; private set; }

        /// <summary>
        /// 出城点
        /// </summary>
        public CityRealAiEventPointInfo OutCityPointInfo { get; private set; }

        /// <summary>
        /// 入城点
        /// </summary>
        public CityRealAiEventPointInfo InCityPointInfo { get; private set; }

        /// <summary>
        /// 是否显示屋顶
        /// </summary>
        public bool RoofShowState { get; private set; }
        
        /// <summary>
        /// 是否达到了显示限制高度
        /// </summary>
        public bool LimitShowState {get; private set;}

        public GameObject NpcMoveObjTemplete { get; private set; }

        private Queue<Action> _scanCompleteCallback;
        
        //延迟创建NPC用
        private IEnumerator _scanIEnumerator;
        private bool _inCity;
        private int _needCreateRoomNPCNum = -1; //待创建的数量，-1为未初始化，为0则创建完毕，>0 时为待创建的数量
        private int _needAreaRoomNPCNum = -1; //待创建的数量，-1为未初始化，为0则创建完毕，>0 时为待创建的数量

        // 小人对象池
        private HashSet<string> _PoolNames;
        
        // 开门营业蓝图
        private GameObject _CityBusinessBlueprint = null;
        private ResLoader _resLoader;
        
        //营业的英雄npc
        private int _heroRoleId;
        private int _heroUid;
        
        // 特殊事件npc列表
        List<CityRealAiNpcController> _hallSpecialEventNpcList = new List<CityRealAiNpcController>();
        private int _specialEvendId;
        
        public void OnInitialize()
        {
            if (!_isInitialize)
            {
                _isInitialize = true;
                AreaDic = new Dictionary<int, CityRealAiAreaManager>();
                StaffAreaDic = new Dictionary<int, CityAreaNpcController>();
                ConfigMgr = new CityRealAiConfigManager();
                ConfigMgr.OnInitialize();
                PoolMgr = new CityRealAiPoolManager();
                PoolMgr.OnInitialize();
                _scanCompleteCallback = new Queue<Action>();
                UnlockBus = true;
                _PoolNames = new HashSet<string>();
                AstarPath.OnLatePostScan += OnLatePostScan;
                InitializeTween();
            }
        }
        
        public void SetNpcMoveCtrObj(LuaTable table)
        {
            NpcMoveObjTemplete = table.Get<int, GameObject>(1);
#if UNITY_EDITOR
            NpcMoveObjTemplete.name = "AI2.0模块NPC模板";
#endif
        }

        public void CreateRoot(LuaTable table)
        {
            if (CityRealAiRootTran == null)
            {
                var cityRootTran = table.Get<string, Transform>("cityRootTran");
                CityRealAiRootTran = new GameObject("CityRealAiRoot").transform;
                CityRealAiRootTran.SetParent(cityRootTran);
                var hudRoot = new GameObject("CityHudRoot");
                hudRoot.SetParent(CityRealAiRootTran);
#if UNITY_EDITOR
                hudRoot.name = "AI2.0模块HudRoot";
#endif
                // hudRoot.transform.localScale = CityRealAiConst.HudScale;
                HudMgr = hudRoot.AddComponent<CityRealHudManager>();
                HudMgr.OnInitialize(ConfigMgr);

                OutCityPointInfo = new CityRealAiEventPointInfo();
                var outCityPointCfg = new CityRealAiEventPointConfig();
                outCityPointCfg.eventId = CityRealAiConst.OutCityEvnetId;
                outCityPointCfg.Position = CityRealAiConst.OutCityPosition;
                outCityPointCfg.Rotate = Vector3.zero;
                OutCityPointInfo.UpdatePointCfg(ref outCityPointCfg);

                InCityPointInfo = new CityRealAiEventPointInfo();
                var inCityPointCfg = new CityRealAiEventPointConfig();
                inCityPointCfg.eventId = CityRealAiConst.InCityEventId;
                inCityPointCfg.Position = CityRealAiConst.OutCityPosition;
                inCityPointCfg.Rotate = Vector3.zero;
                InCityPointInfo.UpdatePointCfg(ref inCityPointCfg);
            }
        }

        public void CreateAllRoomPoints(CityRealAiRoomDatas roomDatas)
        {
            for (int i = 0; i < roomDatas.array.Length; i++)
            {
                var roomData = roomDatas.array[i];
                CreateRoomPoints(roomData);
            }
        }

        //创建移动路点
        public void CreateRoomPoints(CityRealAiRoomData roomData)
        {
            var area = GetOrCreateArea(roomData.areaId);
            if (area != null)
            {
                if (roomData.isDecoration)
                {
                    CreateDecorationPoint(area, roomData.buildingId, roomData.index);
                }
                else
                {
                    CreateRoomPoint(area, roomData.buildingId, roomData.index);
                }
            }
        }

        private void CreateDecorationPoint(CityRealAiAreaManager area, int buildId, int buildIndex)
        {
            var pointRoomCfg = ConfigMgr.GetEventPointRoomCfg(buildId);
            var goods = area.GetOrCreateGoodsCtrl(buildId * 100 + buildIndex);

            for (int i = 0; i < pointRoomCfg.goodsList.Length; i++)
            {
                goods.SetupGoodsName(pointRoomCfg.goodsList[i].GoodsName);
                goods.SetupUnlockBuildId(pointRoomCfg.goodsList[i].UnlockBuildId);
                goods.SetupUnlockLevel(pointRoomCfg.goodsList[i].UnlockLevel);
                goods.SetupFacilityId(pointRoomCfg.goodsList[i].FacilityId);
                for (int j = 0; j < pointRoomCfg.goodsList[i].pointDataList.Length; j++)
                {
                    goods.SetupEventPoint(ref pointRoomCfg.goodsList[i].pointDataList[j]);
                }
            }
        }

        private void CreateRoomPoint(CityRealAiAreaManager area, int buildId, int buildIndex)
        {
            if (area.ContainsRoom(buildId, buildIndex)) // 已创建好该房间的路点
            {
                return;
            }
            
            var room = area.CreateRoom(buildId, buildIndex);
            if (room != null)
            {
                var pointRoomCfg = ConfigMgr.GetEventPointRoomCfg(buildId);

                for (int i = 0; i < pointRoomCfg.birthPointPosList.Length; i++)
                {
                    room.AddBirthPoint(pointRoomCfg.birthPointPosList[i]);
                }


                if (pointRoomCfg.specialPointPosList.Length > 0)
                {
                    room.SetupInRoomPoint(pointRoomCfg.specialPointPosList[0]);
                }
                
                if (pointRoomCfg.specialPointPosList.Length > 1)
                {
                    room.SetupOutRoomPoint(pointRoomCfg.specialPointPosList[1]);
                }
                if (pointRoomCfg.specialPointPosList.Length > 2)
                {
                    room.SetupHidePoint(pointRoomCfg.specialPointPosList[2]);
                }

                if (buildId == CityRealAiConst.WallId && room is CityRealAiWallManager wallMgr)
                {
                    // 大门有绑定后门出入口
                    if (pointRoomCfg.specialPointPosList.Length > 2)
                    {
                        room.SetupInRoomPoint2(pointRoomCfg.specialPointPosList[2]);
                    }
                    if (pointRoomCfg.specialPointPosList.Length > 3)
                    {
                        room.SetupOutRoomPoint2(pointRoomCfg.specialPointPosList[3]);
                    }
                    
                    if (pointRoomCfg.specialPointPosList.Length > 4)
                    {
                        wallMgr.SetupFrontBirthPoint(pointRoomCfg.specialPointPosList[4]);
                    }
                    
                    if (pointRoomCfg.specialPointPosList.Length > 5)
                    {
                        wallMgr.SetupBackBirthPoint(pointRoomCfg.specialPointPosList[5]);
                    }
                    
                    wallMgr.AddBusDownPoints();
                    
                    
                    for (int i = 0; i < pointRoomCfg.FrontStaffWaitPointPosList.Length; i++)
                    {
                        wallMgr.AddFrontStaffWaitPoint(pointRoomCfg.FrontStaffWaitPointPosList[i], pointRoomCfg.FrontStaffWaitPointRotList[i]);
                    }
                    
                    for (int i = 0; i < pointRoomCfg.BackStaffWaitPointPosList.Length; i++)
                    {
                        wallMgr.AddBackStaffWaitPoint(pointRoomCfg.BackStaffWaitPointPosList[i], pointRoomCfg.BackStaffWaitPointRotList[i]);
                    }
                    
                    for (int vecIndex = 0; vecIndex < pointRoomCfg.OccupyAreaPointVecPosList.Length; vecIndex++)
                    {
                        for (int i = 0; i < pointRoomCfg.OccupyAreaPointVecPosList[vecIndex].Length; i++)
                        {
                            wallMgr.AddOccupyAreaBirthPoint(vecIndex, pointRoomCfg.OccupyAreaPointVecPosList[vecIndex][i], pointRoomCfg.OccupyAreaPointVecRotList[vecIndex][i]);
                        }
                    }
                }

                for (int i = 0; i < pointRoomCfg.WaitPointPosList.Length; i++)
                {
                    room.AddWaitPoint(pointRoomCfg.WaitPointPosList[i], pointRoomCfg.WaitPointRotList[i]);
                }

                for (int vecIndex = 0; vecIndex < pointRoomCfg.MultiWaitPointVecPosList.Length; vecIndex++)
                {
                    for (int i = 0; i < pointRoomCfg.MultiWaitPointVecPosList[vecIndex].Length; i++)
                    {
                        room.AddMultiWaitPoint(vecIndex, pointRoomCfg.MultiWaitPointVecPosList[vecIndex][i], pointRoomCfg.MultiWaitPointVecRotList[vecIndex][i]);
                    }
                }

                for (int i = 0; i < pointRoomCfg.BusinessNpcJumpToRoomPointVecPosList.Length; i++)
                {
                    room.AddBusinessNpcJumpToRoomPoint(pointRoomCfg.BusinessNpcJumpToRoomPointVecPosList[i], pointRoomCfg.BusinessNpcJumpToRoomPointVecRotList[i]);
                }

                for (int i = 0; i < pointRoomCfg.MovePointList.Length; i++)
                {
                    room.AddMovePoint(pointRoomCfg.MovePointList[i]);
                }

                var rubbishCfg = ConfigMgr.GetOperateInteractiveConfig(CityRealAiConst.RubbishGoodsCfgId);
                var fixCfg = ConfigMgr.GetOperateInteractiveConfig(CityRealAiConst.FixGoodsCfgId);
                for (int i = 0; i < pointRoomCfg.goodsList.Length; i++)
                {
                    var goods = room.GetOrCreateGoodsCtrl(i);
                    goods.SetupGoodsName(pointRoomCfg.goodsList[i].GoodsName);
                    goods.SetupUnlockBuildId(pointRoomCfg.goodsList[i].UnlockBuildId);
                    goods.SetupUnlockLevel(pointRoomCfg.goodsList[i].UnlockLevel);
                    goods.SetupFacilityId(pointRoomCfg.goodsList[i].FacilityId);
                    for (int j = 0; j < pointRoomCfg.goodsList[i].pointDataList.Length; j++)
                    {
                        var pointInfo = goods.SetupEventPoint(ref pointRoomCfg.goodsList[i].pointDataList[j]);
                        room.AddMovePoint(pointInfo.PointConfig.Position);
                        if (!string.IsNullOrEmpty(pointInfo.PointConfig.MoveGoodsName))
                        {
                            if (!string.IsNullOrEmpty(rubbishCfg.link_point) && pointInfo.PointConfig.MoveGoodsName.Contains(rubbishCfg.link_point))
                            {
                                room.AddRubbishGoods(pointInfo);
                            }
                            else if (!string.IsNullOrEmpty(fixCfg.link_point) && pointInfo.PointConfig.MoveGoodsName.Contains(fixCfg.link_point))
                            {
                                room.AddFixGoods(pointInfo);
                            }
                        }
                    }
                }
            }
        }

        public void RemoveRoomPoints(LuaTable table)
        {
            var areaId = table.Get<string, int>("areaId");

            var area = GetArea(areaId);
            if (area != null)
            {
                var buildId = table.Get<string, int>("buildingId");
                var buildIndex = table.Get<string, int>("index");
                area.RemoveGoodsCtrl(buildId * 100 + buildIndex);
            }
        }

        public void CreateAllRoomNpc(LuaTable table)
        {
            if (_needCreateRoomNPCNum == -1)
            {
                _needCreateRoomNPCNum = 0; 
            }

            if (CityRealAiConst.EnableGpuSkinning)
            {
                // 初始化GPU Skinning，切沙盘的时候可能会销毁
                if (!GPUInstancingManager.GetInstance().IsInitialized())
                {
                    GPUInstancingManager.SetCityPath();
                    GPUInstancingManager.GetInstance().Initialize();
                }
            }

            for (int tableIndex = 1; tableIndex <= table.Length; tableIndex++)
            {
                var config = table.Get<int, LuaTable>(tableIndex);
                CreateRoomNpc(config, false);
            }
            
            RefreshPointMap();
        }

        public void CreateRoomNpc(LuaTable table, bool isNeedRefreshPointMap)
        {
            var buildId = table.Get<string, int>("buildingId");
            var areaId = table.Get<string, int>("areaId");
            var level = table.Get<string, int>("level");
            RoofShowState = table.Get<String, bool>("roofShowState");
            var area = GetArea(areaId);
            if (area == null)
            {
                return;
            }
            var buildIndex = table.Get<string, int>("index");
            var roomMgr = area.GetRoom(buildId, buildIndex);
            if (roomMgr == null)
            {
                return;
            }
            var buildTrans = table.Get<string, Transform>("buildTrans");
            roomMgr.SetupRoomRoot(buildTrans);
            var guestsLimit = table.Get<string, int>("guestsLimit");
            roomMgr.SetupMaxAreaNpcNum(guestsLimit);
            
            AddScanComplete(() =>
            {
                var npcList = table.Get<string, LuaTable>("npcList");
                var roleInfoList = new List<CityRealAiNpcInfo>(npcList.Length);
                for (int index = 1; index <= npcList.Length; index++)
                {
                    var npcInfoTb = npcList.Get<int, LuaTable>(index);
                    var roleInfo = new CityRealAiNpcInfo();
                    roleInfo.cityRoleId = npcInfoTb.Get<string, int>("cityRoleId");
                    roleInfo.isShow = npcInfoTb.Get<string, bool>("isShow");
                    roleInfoList.Add(roleInfo);
                }
                _needCreateRoomNPCNum+=npcList.Length;
                var roomBehavior = ConfigMgr.GetRoleTypeByRoomBehaviorConfig(buildId, level, ref roleInfoList);
                for (int index = 0; index < roleInfoList.Count; index++)
                {
                    var roleInfo = roleInfoList[index];
                    var npcInfoCfg = ConfigMgr.GetAiRoleInfoConfig(roleInfo.cityRoleId);
                    var birthPos = roomMgr.GetWorldPos(roomMgr.HidePointLocalPos);
                    if (roleInfo.isShow)
                    {
                        birthPos = roomMgr.GetWorldPos(roomMgr.GetRandomBirthLocalPos());
                    }
                    var roomNpc = new CityRoomNpcController(roomMgr.ContainerId, npcInfoCfg, birthPos);
                    roomMgr.AddNpcCtrl(roomNpc);
                    area.AddNpcCtrlToAllDic(roomNpc);
                    roomNpc.UpdateServerRoleInfo(roleInfo);
                    var roleType = npcInfoCfg.role_type;
                    if (roomBehavior.room_id != ConfigMgr.EmptyRoomBehaviorCfg.room_id)
                    {
                        roleType = roomBehavior.role_type_array[index];
                    }
                    var roleConfig = ConfigMgr.GetAiRoleConfig(roleType);
                    roomNpc.UpdateRoleConfig(roleConfig);
                    roomNpc.UpdateContainer(roomMgr);
                    roomNpc.OnLoadRoleObj(() =>
                    {
                        _needCreateRoomNPCNum--;
                        roomNpc.ResetNpcScale();
                    });
                }
            });
            roomMgr.SetupScanPointGraph();
            
            if (isNeedRefreshPointMap)
            {
                RefreshPointMap();
            }
        }

        public void CreateAllAreaNpc(LuaTable table)
        {
            //创建区域内随机小人，lua将未被占用的格子随机一个直接发过来
            for (int tableIndex = 1; tableIndex <= table.Length; tableIndex++)
            {
                var config = table.Get<int, LuaTable>(tableIndex);
                CreateAreaNpc(config);
            }
        }

        public void CreateAreaNpc(LuaTable table)
        {
            //初始化待创建的数量
            _needAreaRoomNPCNum = _needAreaRoomNPCNum == -1?0:_needAreaRoomNPCNum;
            
            var areaId = table.Get<string, int>("areaId");
            var area = GetOrCreateArea(areaId);
            AddScanComplete(() =>
            {
                var npcList = table.Get<string, LuaTable>("npcList");
                _needAreaRoomNPCNum += npcList.Length;
                for (int i = 1; i <= npcList.Length; i++)
                {
                    var npcInfoTb = npcList.Get<int, LuaTable>(i);
                    var roleInfo = new CityRealAiNpcInfo();
                    roleInfo.cityRoleId = npcInfoTb.Get<string, int>("cityRoleId");
                    roleInfo.isShow = true;
                    var aId = npcInfoTb.Get<string, int>("areaId");
                    var posTable = npcInfoTb.Get<string, LuaTable>("pos");
                    var x = posTable.Get<string, float>("x");
                    var y = posTable.Get<string, float>("y");
                    var z = posTable.Get<string, float>("z");
                    var pos = new Vector3(x, y, z);
                    var staffId = npcInfoTb.Get<string, int>("staffId");
                    var hud = npcInfoTb.Get<string, string>("hud");
                    var hudNot = npcInfoTb.Get<string, string>("hudNot");
                    var canHire = npcInfoTb.Get<string, bool>("canHire");

                    
                    var npcInfoCfg = ConfigMgr.GetAiRoleInfoConfig(roleInfo.cityRoleId);
                    var areaNpc = new CityAreaNpcController(area.ContainerId, npcInfoCfg, pos);
                    areaNpc.areaId = aId;
                    areaNpc.staffId = staffId;
                    area.AddNpcCtrl(areaNpc);
                    area.AddNpcCtrlToAllDic(areaNpc);
                    StaffAreaDic[staffId] = areaNpc;
                    areaNpc.UpdateServerRoleInfo(roleInfo);
                    var roleConfig = ConfigMgr.GetAiRoleConfig(npcInfoCfg.role_type);
                    areaNpc.UpdateRoleConfig(roleConfig);
                    areaNpc.UpdateContainer(area);
                    areaNpc.OnLoadRoleObj(() =>
                    {
                        _needAreaRoomNPCNum--;
                        areaNpc.ResetNpcScale();
  
                        // //将需要创建hud的参数直接传入
                        // HudMgr.OnShowStaff(areaNpc.Uid, CityRealAiConst.StaffBubble, areaNpc.GetRoleObj().transform, 
                        //     staffId, CityRealAiConst.DefaultBubbleBone);
                    });
                }
            });
            
            TriggerCreateNpcTaskCoroutine();
        }
        
        public void OnUnlockNpc(LuaTable table)
        {
            var buildId = table.Get<string, int>("buildingId");
            var areaId = table.Get<string, int>("areaId");
            var level = table.Get<string, int>("level");
            RoofShowState = table.Get<String, bool>("roofShowState");
            var area = GetArea(areaId);
            if (area == null)
            {
                return;
            }
            var buildIndex = table.Get<string, int>("index");
            var roomMgr = area.GetRoom(buildId, buildIndex);
            if (roomMgr == null)
            {
                return;
            }
            var guestsLimit = table.Get<string, int>("guestsLimit");
            roomMgr.SetupMaxAreaNpcNum(guestsLimit);
            AddScanComplete(() =>
            {
                var npcList = table.Get<string, LuaTable>("npcList");
                var roleInfoList = new List<CityRealAiNpcInfo>(npcList.Length);
                for (int i = 1; i <= npcList.Length; i++)
                {
                    var npcInfoTb = npcList.Get<int, LuaTable>(i);
                    var roleInfo = new CityRealAiNpcInfo();
                    roleInfo.cityRoleId = npcInfoTb.Get<string, int>("cityRoleId");
                    roleInfo.isShow = npcInfoTb.Get<string, bool>("isShow");
                    roleInfoList.Add(roleInfo);
                }

                var excludeUidList = new List<int>();

                var roomBehavior = ConfigMgr.GetRoleTypeByRoomBehaviorConfig(buildId, level, ref roleInfoList);
                for (int index = 0; index < roleInfoList.Count; index++)
                {
                    var roleInfo = roleInfoList[index];
                    CityRealAiNpcController npcCtrl = null;
                    var npcInfoCfg = ConfigMgr.GetAiRoleInfoConfig(roleInfo.cityRoleId);
                    if (npcInfoCfg.role_tag == CityRealAiConst.TransportNpcTag ||
                        npcInfoCfg.role_tag == CityRealAiConst.UpgradeNpcTag)
                    {
                        npcCtrl = GetNpcCtrlExcludeUid(roleInfo.cityRoleId, buildId, excludeUidList);
                    }
                    else
                    {
                        npcCtrl = roomMgr.GetNpcCtrlExcludeUid(roleInfo.cityRoleId, excludeUidList);
                    }
                    if (npcCtrl == null)
                    {
                        var birthPos = roomMgr.GetWorldPos(roomMgr.HidePointLocalPos);
                        if (roleInfo.isShow)
                        {
                            birthPos = roomMgr.GetWorldPos(roomMgr.GetRandomBirthLocalPos());
                        }
                        npcCtrl = new CityRoomNpcController(roomMgr.ContainerId, npcInfoCfg, birthPos);
                        excludeUidList.Add(npcCtrl.Uid);
                        roomMgr.AddNpcCtrl(npcCtrl);
                        area.AddNpcCtrlToAllDic(npcCtrl);
                        npcCtrl.UpdateServerRoleInfo(roleInfo);
                        var roleType = npcInfoCfg.role_type;
                        if (roomBehavior.room_id != ConfigMgr.EmptyRoomBehaviorCfg.room_id)
                        {
                            roleType = roomBehavior.role_type_array[index];
                        }
                        var roleConfig = ConfigMgr.GetAiRoleConfig(roleType);
                        npcCtrl.UpdateRoleConfig(roleConfig);
                        npcCtrl.UpdateContainer(roomMgr);
                        npcCtrl.OnLoadRoleObj(() =>
                        {
                            npcCtrl.ResetNpcScale();
                        });
                    }
                    else
                    {
                        excludeUidList.Add(npcCtrl.Uid);
                        if (roomBehavior.room_id != ConfigMgr.EmptyRoomBehaviorCfg.room_id)
                        {
                            npcCtrl.UpdateServerRoleInfo(roleInfo);
                            var roleType = roomBehavior.role_type_array[index];
                            var roleConfig = ConfigMgr.GetAiRoleConfig(roleType);
                            npcCtrl.UpdateRoleConfig(roleConfig);
                        }
                    }

                    if (npcCtrl.RoleClassType == (int)CityRealNpcClassType.DJ)
                    {
                        _heroRoleId = npcCtrl.RoleId;
                        _heroUid = npcCtrl.Uid;
                    }
                }
            });
            TriggerCreateNpcTaskCoroutine();
        }

        public void CreateCastleHeroNpc(LuaTable table)
        {
            OnUnlockNpc(table);
        }

        public void PlayNpcBreathEffect(int areaId, int buildId, int buildIndex, int cityRoleId, bool isShow)
        {
            var area = GetArea(areaId);
            if (area == null)
            {
                return;
            }
            var roomMgr = area.GetRoom(buildId, buildIndex);
            if (roomMgr == null)
            {
                return;
            }

            CityRealAiNpcController npcCtrl = null;
            var npcInfoCfg = ConfigMgr.GetAiRoleInfoConfig(cityRoleId);
            if (npcInfoCfg.role_tag == CityRealAiConst.TransportNpcTag ||
                npcInfoCfg.role_tag == CityRealAiConst.UpgradeNpcTag)
            {
                npcCtrl = GetNpcCtrlExcludeUid(cityRoleId, buildId, null);
            }
            else
            {
                npcCtrl = roomMgr.GetNpcCtrlExcludeUid(cityRoleId, null);
            }
            
            if (npcCtrl != null)
            {
                float breathLight = isShow ? 1 : 0;
                npcCtrl.SetBreathLight(breathLight);
            }
        }

        public CityRealAiNpcController GetNpcController(int areaId, int buildId, int buildIndex, int cityRoleId)
        {
            var area = GetArea(areaId);
            if (area == null)
            {
                return null;
            }
            var roomMgr = area.GetRoom(buildId, buildIndex);
            if (roomMgr == null)
            {
                return null;
            }

            CityRealAiNpcController npcCtrl = null;
            var npcInfoCfg = ConfigMgr.GetAiRoleInfoConfig(cityRoleId);
            if (npcInfoCfg.role_tag == CityRealAiConst.TransportNpcTag ||
                npcInfoCfg.role_tag == CityRealAiConst.UpgradeNpcTag)
            {
                npcCtrl = GetNpcCtrlExcludeUid(cityRoleId, buildId, null);
            }
            else
            {
                npcCtrl = roomMgr.GetNpcCtrlExcludeUid(cityRoleId, null);
            }

            return npcCtrl;
        }

        public CityAreaNpcController CreateCustomer(CityRealAiNpcInfo roleInfo, int busIndex, Vector3 pos, CityBusinessType businessType, bool isRestore, bool isJumpToRoom = false)
        {
            var area = GetOrCreateArea(CityRealAiConst.WallAreaId);
            var buildId = CityRealAiConst.WallId;
            if (isJumpToRoom)
            {
                if (roleInfo.buildingId > 0)
                {
                    buildId = roleInfo.buildingId;
                }
                else
                {
                    buildId = CityRealAiConst.HallId;
                }
            }
            var roomMgr = area.GetRoom(buildId, 1);
            var npcInfoCfg = ConfigMgr.GetAiRoleInfoConfig(roleInfo.cityRoleId);
            var areaNpc = new CityAreaNpcController(area.ContainerId, npcInfoCfg, pos);
            roomMgr.AddNpcCtrl(areaNpc);
            area.AddNpcCtrlToAllDic(areaNpc);
            areaNpc.UpdateServerRoleInfo(roleInfo);
            var roleConfig = ConfigMgr.GetAiRoleConfig(npcInfoCfg.role_type);
            areaNpc.UpdateRoleConfig(roleConfig);
            areaNpc.UpdateContainer(roomMgr);
            areaNpc.SetBusIndex(busIndex);
            areaNpc.SetBusinessType(businessType);
            areaNpc.SetIsRestoreCustomer(isRestore);
            areaNpc.SetupIsJumpToRoomCustomer(isJumpToRoom);
            areaNpc.OnLoadRoleObj(() =>
            {
                areaNpc.ResetNpcScale();
                if (roleInfo.isNew)
                {
                    CityRealAiCSharpCallLuaManager.LoadNewNpc(roleInfo.cityRoleId);
                }
            });
            return areaNpc;
        }
        
        // 创建区域占领的表演小人(雇员、顾客)
        public void CreateUnlockAreaPerformNpc(LuaTable table)
        {
            //初始化待创建的数量
            _needAreaRoomNPCNum = _needAreaRoomNPCNum == -1?0:_needAreaRoomNPCNum;
            
            AddScanComplete(() =>
            {
                var npcList = table.Get<string, LuaTable>("npcList");
                _needAreaRoomNPCNum += npcList.Length;
                for (int i = 1; i <= npcList.Length; i++)
                {
                    var npcInfoTb = npcList.Get<int, LuaTable>(i);
                    var roleInfo = new CityRealAiNpcInfo();
                    roleInfo.cityRoleId = npcInfoTb.Get<string, int>("cityRoleId");
                    roleInfo.isShow = true;
                    var areaId = npcInfoTb.Get<string, int>("areaId");
                    var posTable = npcInfoTb.Get<string, LuaTable>("pos");
                    var x = posTable.Get<string, float>("x");
                    var y = posTable.Get<string, float>("y");
                    var z = posTable.Get<string, float>("z");
                    var pos = new Vector3(x, y, z);
                    var staffId = npcInfoTb.Get<string, int>("staffId");
                    var hideStaffTreeId = npcInfoTb.Get<string, int>("hideStaffTreeId");
                    var isRestore = npcInfoTb.Get<string, bool>("isRestore");
                    var hud = npcInfoTb.Get<string, string>("hud");
                    bool isStaff = staffId > 0;
                    
                    if (isStaff && StaffAreaDic.ContainsKey(staffId))
                    {
                        // 已经创建过
                        _needAreaRoomNPCNum--;
                        continue;
                    }

                    var area = GetOrCreateArea(areaId);
                    var wallMgr = GetCityRealAiWallManager();
                    var npcInfoCfg = ConfigMgr.GetAiRoleInfoConfig(roleInfo.cityRoleId);
                    var areaNpc = new CityAreaNpcController(area.ContainerId, npcInfoCfg, pos);
                    areaNpc.IsUnlockAreaPerform = true;
                    wallMgr.AddNpcCtrl(areaNpc);
                    area.AddNpcCtrlToAllDic(areaNpc);
                    if (isStaff)
                    {
                        // 雇员
                        areaNpc.staffId = staffId;
                        areaNpc.hideStaffTreeId = hideStaffTreeId;
                        StaffAreaDic[staffId] = areaNpc;
                    }
                    else
                    {
                        // 顾客
                        
                    }
                    areaNpc.areaId = areaId;

                    areaNpc.UpdateServerRoleInfo(roleInfo);
                    var roleConfig = ConfigMgr.GetAiRoleConfig(npcInfoCfg.role_type);
                    areaNpc.UpdateRoleConfig(roleConfig);
                    areaNpc.UpdateContainer(wallMgr);
                    areaNpc.OnLoadRoleObj(() =>
                    {
                        _needAreaRoomNPCNum--;
                        
                        areaNpc.Seeker.SetIsInRoomAction(false);     // 切换区域寻路
                        areaNpc.SetNpcScale(3.76f);
                        areaNpc.Seeker.transform.localEulerAngles = new Vector3(0, 180, 0);
                        // 执行区域解锁事件
                        if (isRestore)
                        {
                            if (isStaff)
                            {
                                CityRealAiManager.GetInstance().HudMgr.OnReShowStaff(areaNpc.Uid, CityRealAiConst.StaffBubble, areaNpc.GetRoleObj().transform, 
                                    staffId, CityRealAiConst.DefaultBubbleBone);
                            }
                        }
                        else
                        {
                            areaNpc.ExecuteUnlockAreaEvent();
                        }
                    });
                }
            });
            
            TriggerCreateNpcTaskCoroutine();
        }
        
        // 创建前门等待雇佣的雇员
        public void CreateFrontEmployStaffNpc(LuaTable table)
        {
            //初始化待创建的数量
            _needAreaRoomNPCNum = _needAreaRoomNPCNum == -1?0:_needAreaRoomNPCNum;
            
            AddScanComplete(() =>
            {
                var npcList = table.Get<string, LuaTable>("npcList");
                _needAreaRoomNPCNum += npcList.Length;
                for (int i = 1; i <= npcList.Length; i++)
                {
                    var npcInfoTb = npcList.Get<int, LuaTable>(i);
                    var roleInfo = new CityRealAiNpcInfo();
                    roleInfo.cityRoleId = npcInfoTb.Get<string, int>("cityRoleId");
                    roleInfo.isShow = true;
                    var areaId = npcInfoTb.Get<string, int>("areaId");
                    var posTable = npcInfoTb.Get<string, LuaTable>("pos");
                    var x = posTable.Get<string, float>("x");
                    var y = posTable.Get<string, float>("y");
                    var z = posTable.Get<string, float>("z");
                    var pos = new Vector3(x, y, z);
                    var staffId = npcInfoTb.Get<string, int>("staffId");
                    var hideStaffTreeId = npcInfoTb.Get<string, int>("hideStaffTreeId");
                    var hud = npcInfoTb.Get<string, string>("hud");
                    bool isStaff = staffId > 0;
                    
                    if (isStaff && StaffAreaDic.ContainsKey(staffId))
                    {
                        // 已经创建过
                        _needAreaRoomNPCNum--;
                        continue;
                    }

                    var area = GetOrCreateArea(areaId);
                    var wallMgr = GetCityRealAiWallManager();
                    var npcInfoCfg = ConfigMgr.GetAiRoleInfoConfig(roleInfo.cityRoleId);
                    var areaNpc = new CityAreaNpcController(area.ContainerId, npcInfoCfg, pos);
                    areaNpc.IsUnlockAreaPerform = true;
                    wallMgr.AddNpcCtrl(areaNpc);
                    area.AddNpcCtrlToAllDic(areaNpc);
                    if (isStaff)
                    {
                        // 雇员
                        areaNpc.staffId = staffId;
                        areaNpc.hideStaffTreeId = hideStaffTreeId;
                        StaffAreaDic[staffId] = areaNpc;
                    }
                    areaNpc.areaId = areaId;

                    areaNpc.UpdateServerRoleInfo(roleInfo);
                    var roleConfig = ConfigMgr.GetAiRoleConfig(npcInfoCfg.role_type);
                    areaNpc.UpdateRoleConfig(roleConfig);
                    areaNpc.UpdateContainer(wallMgr);
                    areaNpc.OnLoadRoleObj(() =>
                    {
                        _needAreaRoomNPCNum--;
                        areaNpc.ResetNpcScale();

                        // 执行前门雇员等待雇佣事件
                        areaNpc.ExecuteFrontEmployEvent();
                    });
                }
            });
            
            TriggerCreateNpcTaskCoroutine();
        }

        private CityRealAiNpcController GetNpcCtrlExcludeUid(int roleId, int birthContainerId, List<int> excludeUidList)
        {
            foreach (var keyValuePair in AreaDic)
            {
                var npcCtrl = keyValuePair.Value.GetNpcCtrlExcludeUid(roleId, birthContainerId, excludeUidList);
                if (npcCtrl != null)
                {
                    return npcCtrl;
                }
            }

            return null;
        }
        
        public CityRealAiEventPointInfo CreateAreaVirtualGoodsCtrl(int uid, int treeId, int eventId, int goodsType)
        {
            if (AreaDic == null || AreaDic.Count <= 0)
            {
                return null;
            }
            var areaIndex = Random.Range(0, AreaDic.Count);
            var index = 0;
            foreach (var keyValuPair in AreaDic)
            {
                if (areaIndex == index)
                {
                    var randomPos = CityRealAiCSharpCallLuaManager.GetAreaRandomPos(keyValuPair.Key, uid);
                   return keyValuPair.Value.CreateVirtualGoodsCtrl(uid, treeId, eventId, goodsType, randomPos);
                }

                index++;
            }

            return null;
        }
        public CityRealAiEventPointInfo CreateAreaRandomPointCtrl(int uid, int treeId, int eventId,int goodsType, int areaId, int staffId)
        {
            if (AreaDic == null || AreaDic.Count <= 0)
            {
                return null;
            }
            var randomPos = CityRealAiCSharpCallLuaManager.GetAreaRandomPos(areaId, staffId);
            return AreaDic[areaId].CreateVirtualGoodsCtrl(uid, treeId, eventId, goodsType,randomPos);
        }
        
        public CityRealAiEventPointInfo CreateAreaFixedPointCtrl(int uid, int treeId, int eventId,int goodsType, int areaId, Vector3 pos)
        {
            if (AreaDic == null || AreaDic.Count <= 0)
            {
                return null;
            }
            return AreaDic[areaId].CreateVirtualGoodsCtrl(uid, treeId, eventId, goodsType, pos);
        }

        public void OnBusinessBuildingLevelUp(int buildId, int buildIndex)
        {
            var area = GetArea(CityRealAiConst.WallAreaId);
            if (area == null)
            {
                return;
            }
            
            var roomMgr = area.GetRoom(buildId, buildIndex);
            if (roomMgr == null)
            {
                return;
            }

            // 处理刷新建筑容量，让排队的小人进去
            while (!roomMgr.IsRealFull && roomMgr.WaitQueueCount>0)
            {
                roomMgr.TriggerWaitQueueMove();
            }
        }

        public void HideRubbish(int areaId, int buildId, int buildIndex, int uid, float duration)
        {
            var area = GetArea(areaId);
            if (area == null)
            {
                return;
            }
            var roomMgr = area.GetRoom(buildId, buildIndex);
            if (roomMgr == null)
            {
                return;
            }
            
            roomMgr.HideRubbish(uid, duration);
        }
        
        public void HideFix(int areaId, int buildId, int buildIndex, int uid, float duration)
        {
            var area = GetArea(areaId);
            if (area == null)
            {
                return;
            }
            var roomMgr = area.GetRoom(buildId, buildIndex);
            if (roomMgr == null)
            {
                return;
            }
            
            roomMgr.HideFix(uid, duration);
        }

        private void AddScanComplete(Action scanComplete)
        {
            if (AstarPath.active)
            {
                PointGraph graph = AstarPath.active.data.pointGraph;
                if (graph != null)
                {
                    _scanCompleteCallback.Enqueue(scanComplete);
                }
            }
        }
        
        public bool CheckAiCreateComplete()
        {
            return _needCreateRoomNPCNum == 0  && _needAreaRoomNPCNum == 0;
        }
        
        private void RefreshPointMap()
        {
            if (AstarPath.active)
            {
                PointGraph graph = AstarPath.active.data.pointGraph;
                if (graph != null)
                {
                    AstarPath.active.Scan(graph);
                }
            }
        }

        private void OnLatePostScan(AstarPath script)
        {
            TriggerCreateNpcTaskCoroutine();
        }

        private void TriggerCreateNpcTaskCoroutine()
        {
            if (_scanCompleteCallback != null && _scanCompleteCallback.Count > 0)
            {
                if (this._scanIEnumerator == null)
                {
                    this._scanIEnumerator =  handScanCompleteCallback(_scanCompleteCallback);
                    Timers.inst.StartCoroutine(this._scanIEnumerator);
                }
            }
        }

        private IEnumerator handScanCompleteCallback(Queue<Action> scanCallBacks)
        {
            if (scanCallBacks != null)
            {
                if (CityRealAiConst.EnableGpuSkinning)
                {
                    // 等待GPU Skinning初始化完成
                    while (!GPUInstancingManager.GetInstance().IsInitialized())
                    {
                        yield return null;
                    }
                }

                while (scanCallBacks.Count > 0)
                {
                    if (this._inCity)
                    {
                        Action callback = scanCallBacks.Dequeue();
                        callback.Invoke();
                    }
                    yield return new WaitForSeconds(0.1f);
                }
                scanCallBacks.Clear();
                if (this._scanIEnumerator != null) //TODO: 结束了，area还在添加
                {
                    Timers.inst.StopCoroutine(this._scanIEnumerator);
                    this._scanIEnumerator = null;
                }
               
            }
           
        }

        public void StartSpecialStateTimer()
        {
            foreach (var keyValuPair in AreaDic)
            {
                keyValuPair.Value.StartSpecialStateTimer();
            }
        }

        public void OnAddQueue(LuaTable table)
        {
            var areaId = table.Get<string, int>("areaId");
            var buildingId = table.Get<string, int>("buildingId");
            var buildIndex = table.Get<string, int>("buildIndex");
            var mainType = table.Get<string, int>("mainType");

            // mainType 为 lua 层 L_Enum.QueueMainType，不重复定义了
            if (mainType == 1) // 建筑升级队列
            {
                TriggerRoomStateChangeEvent(areaId, buildingId, buildIndex, CityRealRoomStateChangeEvent.StartUpgrade);
            }
            else if (mainType == 2) // 研究室研究队列
            {
                TriggerRoomStateChangeEvent(areaId, buildingId, buildIndex, CityRealRoomStateChangeEvent.StartResearch);
            }
            else if (mainType == 4) // 兵营训练队列
            {
                TriggerRoomStateChangeEvent(areaId, buildingId, buildIndex, CityRealRoomStateChangeEvent.StartTrain);
            }
            else if (mainType == 5) // 医院治疗队列
            {
                TriggerRoomStateChangeEvent(areaId, buildingId, buildIndex, CityRealRoomStateChangeEvent.StartTreat);
            }
        }

        public void OnRemoveQueue(LuaTable table)
        {
            var areaId = table.Get<string, int>("areaId");
            var buildingId = table.Get<string, int>("buildingId");
            var buildIndex = table.Get<string, int>("buildIndex");
            var mainType = table.Get<string, int>("mainType");
            
            // mainType 为 lua 层 L_Enum.QueueMainType，不重复定义了
            if (mainType == 1) // 建筑升级队列
            {
                TriggerRoomStateChangeEvent(areaId, buildingId, buildIndex, CityRealRoomStateChangeEvent.EndUpgrade);
            }
            else if (mainType == 2) // 研究室研究队列
            {
                TriggerRoomStateChangeEvent(areaId, buildingId, buildIndex, CityRealRoomStateChangeEvent.EndResearch);
            }
            else if (mainType == 4) // 兵营训练队列
            {
                TriggerRoomStateChangeEvent(areaId, buildingId, buildIndex, CityRealRoomStateChangeEvent.EndTrain);
            }
            else if (mainType == 5) // 医院治疗队列
            {
                TriggerRoomStateChangeEvent(areaId, buildingId, buildIndex, CityRealRoomStateChangeEvent.EndTreat);
            }
        }

        public void OnWoundedSoldierChanged(LuaTable table)
        {
            var areaId = table.Get<string, int>("areaId");
            var buildingId = table.Get<string, int>("buildingId");
            var buildIndex = table.Get<string, int>("buildIndex");
            var isExistWounded = table.Get<string, bool>("isExistWounded");
            if (isExistWounded)
            {
                TriggerRoomStateChangeEvent(areaId, buildingId, buildIndex, CityRealRoomStateChangeEvent.ExistWoundedSoldier);
            }
            else
            {
                TriggerRoomStateChangeEvent(areaId, buildingId, buildIndex, CityRealRoomStateChangeEvent.NotWoundedSoldier);
            }
        }
        
        public  void OnAssemblyChanged(LuaTable table)
        {
            var areaId = table.Get<string, int>("areaId");
            var buildingId = table.Get<string, int>("buildingId");
            var buildIndex = table.Get<string, int>("buildIndex");
            var isExistAssembly = table.Get<string, bool>("isExistAssembly");
            if (isExistAssembly)
            {
                TriggerRoomStateChangeEvent(areaId, buildingId, buildIndex, CityRealRoomStateChangeEvent.StartAssembly);
            }
            else
            {
                TriggerRoomStateChangeEvent(areaId, buildingId, buildIndex, CityRealRoomStateChangeEvent.EndAssembly);
            }
        }
        
        public  void OnReinforceChanged(LuaTable table)
        {
            var areaId = table.Get<string, int>("areaId");
            var buildingId = table.Get<string, int>("buildingId");
            var buildIndex = table.Get<string, int>("buildIndex");
            var isExistReinforce = table.Get<string, bool>("isExistAidMember");
            if (isExistReinforce)
            {
                TriggerRoomStateChangeEvent(areaId, buildingId, buildIndex, CityRealRoomStateChangeEvent.StartReinforce);
            }
            else
            {
                TriggerRoomStateChangeEvent(areaId, buildingId, buildIndex, CityRealRoomStateChangeEvent.EndReinforece);
            }
        }

        private void TriggerRoomStateChangeEvent(int areaId, int buildId, int buildIndex, CityRealRoomStateChangeEvent changeEvent)
        {
            var area = GetArea(areaId);
            if (area != null)
            {
                area.TriggerRoomStateChangeEvent(buildId, buildIndex, changeEvent);
            }
        }

        public void OnReStartCity()
        {
            if (AreaDic != null)
            {
                foreach (var keyValuePair in AreaDic)
                {
                    keyValuePair.Value.OnPause(false);
                }
            }

            if (!Timers.inst.Exists(OnInterval))
            {
                Timers.inst.Add(CityRealAiConst.UpdateDeltaTime, -1, OnInterval);
            }
            _inCity = true;
        }

        public void OnStopCity()
        {
            if (AreaDic != null)
            {
                foreach (var keyValuePair in AreaDic)
                {
                    keyValuePair.Value.OnPause(true);
                }
            }
            
            _inCity = false;
            Timers.inst.Remove(OnInterval);
        }

        public void OnReStartRoomAllNpc(LuaTable table)
        {
            var lastAreaId = table.Get<string, int>("lastAreaId");
            var areaId = table.Get<string, int>("areaId");
            var buildingId = table.Get<string, int>("buildingId");
            var buildIndex = table.Get<string, int>("buildIndex");
            var needRefreshPoint = table.Get<string, bool>("needRefreshPoint");
            CityRealAiRoomManager room = null;
            if (lastAreaId == areaId)
            {
                var area = GetArea(areaId);
                if (area != null)
                {
                    room = area.TryGetRoom(buildingId, buildIndex);
                }
            }
            else
            {
                var lastArea = GetArea(lastAreaId);
                var newArea = GetOrCreateArea(areaId);
                if (lastArea != null)
                {
                    room = lastArea.TryGetRoom(buildingId, buildIndex);
                    if (room != null)
                    {
                        lastArea.RemoveRoom(buildingId, buildIndex);
                        newArea.AddRoom(room);
                    }
                }
            }
            
            if (room != null)
            {
                if (needRefreshPoint)
                {
                    room.UpdateNavPointsPosition();
                    AddScanComplete(room.OnReStartRoomAllNpc);
                    RefreshPointMap();
                }
                else
                {
                    room.OnReStartRoomAllNpc();
                }
            }
        }

        public void OnStopRoomAllNpc(LuaTable table)
        {
            var areaId = table.Get<string, int>("areaId");
            var buildingId = table.Get<string, int>("buildingId");
            var buildIndex = table.Get<string, int>("buildIndex");
            var area = GetArea(areaId);
            if (area != null)
            {
                var room = area.TryGetRoom(buildingId, buildIndex);
                if (room != null)
                {
                    room.OnStopRoomAllNpc();
                }
            }
        }
        
        public void LoadChangeShowOrHideAllNpc(LuaTable table)
        {
            var state = table.Get<string, bool>("state");
            RoofShowState = !state;
            if (AreaDic == null)
            {
                return;
            }
            foreach (var keyValuePair in AreaDic)
            {
                foreach (var subKeyValuePair in keyValuePair.Value.RoomDic)
                {
                    foreach (var roomKeyValurPair in subKeyValuePair.Value)
                    {
                        // if (roomKeyValurPair.Value.ContainerId == CityRealAiConst.WallId)
                        // {
                        //     // 跳过城墙
                        //     continue;
                        // }
                        roomKeyValurPair.Value.ShowOrHideAllNpc(!(RoofShowState && LimitShowState));
                    }
                }
            }
        }

        public void ZoomChangeShowOrHideAllNpc(bool state)
        {
            LimitShowState = !state;
            if (AreaDic == null)
            {
                return;
            }

            bool isVisible = !(RoofShowState && LimitShowState);
            foreach (var keyValuePair in AreaDic)
            {
                foreach (var subKeyValuePair in keyValuePair.Value.RoomDic)
                {
                    foreach (var roomKeyValurPair in subKeyValuePair.Value)
                    {
                        roomKeyValurPair.Value.ShowOrHideAllNpc(isVisible);
                    }
                }
                foreach (var subKeyValuePair in keyValuePair.Value.AllNpcTypeCtrlDic)
                {
                    foreach (var valurPair in subKeyValuePair.Value)
                    {
                        valurPair.Value.ShowOrHide(isVisible);
                    }
                }
                
            }
        }

        public void OnLodChangeShowOrHideRoomAllNpc(LuaTable table)
        {
            var areaId = table.Get<string, int>("areaId");
            var buildingId = table.Get<string, int>("buildingId");
            var buildIndex = table.Get<string, int>("buildIndex");
            var state = table.Get<string, bool>("state");
            RoofShowState = !state;
            var area = GetArea(areaId);
            if (area != null)
            {
                var room = area.TryGetRoom(buildingId, buildIndex);
                if (room != null)
                {
                    room.ShowOrHideAllNpc(!(RoofShowState && LimitShowState));
                }
            }
        }
        
        public void OnUnlockBus(LuaTable table)
        {
            UnlockBus = table.Get<string, bool>("unlockBus");
        }

        public void OnExitCity()
        {
            if (!_isInitialize)
            {
                return;
            }

            this._inCity = false;
            Timers.inst.Remove(OnInterval);
            
            if (this._scanIEnumerator != null)
            {
                Timers.inst.StopCoroutine(this._scanIEnumerator);
                this._scanIEnumerator = null;
            }

            _needCreateRoomNPCNum = -1;
            _needAreaRoomNPCNum = -1;
            //清理延迟创建
            if (_scanCompleteCallback != null)
            {
                _scanCompleteCallback.Clear();
            }
            if (AreaDic != null)
            {
                foreach (var keyValuePair in AreaDic)
                {
                    keyValuePair.Value.OnResetNpc();
                }
            }

            if (StaffAreaDic != null)
            {
                StaffAreaDic.Clear();
            }

            if (HudMgr != null)
            {
                HudMgr.OnReset();
            }

            // 卸掉对象池资源
            foreach (string path in _PoolNames)
            {
                GameObjectPoolManager.GetInstance().Destroy(path);
            }
            _PoolNames.Clear();
        }

        /// <summary>
        /// 获取城门
        /// </summary>
        /// <returns></returns>
        public CityRealAiWallManager GetCityRealAiWallManager()
        {
            var area = GetOrCreateArea(CityRealAiConst.WallAreaId);
            if (area != null)
            {
                var room = area.TryGetRoom(CityRealAiConst.WallId, 1);
                return room as CityRealAiWallManager;
            }
            return null;
        }

        /// <summary>
        /// 获取大厅
        /// </summary>
        /// <returns></returns>
        public CityRealAiRoomManager GetCityRealAiHall()
        {
            var area = GetOrCreateArea(CityRealAiConst.WallAreaId);
            var room = area.TryGetRoom(CityRealAiConst.HallId, 1);
            return room;
        }

        public CityRealAiRoomManager GetCityRealAiMansionLivingRoom()
        {
            var area = GetOrCreateArea(CityRealAiConst.WallAreaId);
            var room = area.TryGetRoom(CityRealAiConst.MansionLivingRoomId, 1);
            return room;
        }

        /// <summary>
        /// 开门营业
        /// </summary>
        /// <param name="table"></param>
        public void OpenBusiness(LuaTable table)
        {
            if (!_CityBusinessBlueprint)
            {
                LoadResource(CityRealAiConst.CityBusinessBlueprint, asset =>
                {
                    if (!_CityBusinessBlueprint)
                    {
                        _CityBusinessBlueprint = (GameObject)GameObject.Instantiate((UnityEngine.Object) asset);
                        GameObject.DontDestroyOnLoad(_CityBusinessBlueprint);
                    }

                    _openBusiness(table);
                });
            }
            else
            {
                _openBusiness(table);
            }
        }
        void _openBusiness(LuaTable table)
        {
            CityRealAiConst.IsInOpenBusiness = true;
            CityRealAiWallManager wallMgr = GetCityRealAiWallManager();
            if (wallMgr!=null)
            {
                wallMgr.OpenBusiness(table);
                int openedNum=table.Get<string, int>("openedNum");
                if (openedNum==null||openedNum <= 0)
                {
                    return;
                }
                bool isSkipAni = table.Get<string, bool>("isSkipAni");
                if (!isSkipAni)
                {
                    Graph.SendGlobalEvent(CityRealAiConst.CityBusinessOpenBusinessEventName, null, null);
                }
                else
                {
                    Graph.SendGlobalEvent(CityRealAiConst.CityBusinessSkipTimelineEventName, null, null);
                }
            }
        }

        public void TimelineJumpEvent()
        {
            CityRealAiWallManager wallMgr = GetCityRealAiWallManager();
            if (wallMgr != null)
            {
                wallMgr.NpcJumpToRoomNearby();
            }
        }

        /// <summary>
        /// 关闭营业
        /// </summary>
        public void CloseBusiness()
        {
            CityRealAiConst.IsInOpenBusiness = false;
            CityRealAiWallManager wallMgr = GetCityRealAiWallManager();
            if (wallMgr!=null)
            {
                wallMgr.CloseBusiness();
            }

            if (AreaDic != null)
            {
                foreach (var keyValuePair in AreaDic)
                {
                    keyValuePair.Value.OnCloseBusiness();
                }
            }
            if (_CityBusinessBlueprint)
            {
                GameObject.Destroy(_CityBusinessBlueprint);
                _CityBusinessBlueprint = null;
            }
            // 结束营业的时候，移除夜总会内的英雄小人
            var area = GetArea(CityRealAiConst.WallAreaId);
            if (area == null)
            {
                return;
            }
            var roomMgr = area.GetRoom(CityRealAiConst.HallId, 1);
            var npcCtrl = roomMgr.GetNpcCtrl(_heroRoleId, _heroUid);
            roomMgr.RemoveNpcCtrl(_heroRoleId, _heroUid);
            if (npcCtrl != null)
            {
                npcCtrl.Dispose();
            }
        }
        
        /// <summary>
        /// 恢复开门营业状态
        /// </summary>
        /// <param name="table"></param>
        public void RestoreOpenBusiness(LuaTable table)
        {
            CityRealAiConst.IsInOpenBusiness = true;
            CityRealAiWallManager wallMgr = GetCityRealAiWallManager();
            if (wallMgr!=null)
            {
                wallMgr.RestoreOpenBusiness(table);
            }
        }

        public void ClearOpenBusiness()
        {
            CityRealAiWallManager wallMgr = GetCityRealAiWallManager();
            if (wallMgr!=null)
            {
                wallMgr.ClearOpenBusiness();
            }
        }
        
        /// <summary>
        /// 自动营业
        /// </summary>
        /// <param name="table"></param>
        public void OpenAutoBusiness(LuaTable table)
        {
            CityRealAiWallManager wallMgr = GetCityRealAiWallManager();
            if (wallMgr!=null)
            {
                wallMgr.OpenAutoBusiness(table);
            }
        }

        /// <summary>
        /// 关闭自动营业
        /// </summary>
        public void CloseAutoBusiness()
        {
            CityRealAiConst.IsInOpenBusiness = false;
            CityRealAiWallManager wallMgr = GetCityRealAiWallManager();
            if (wallMgr!=null)
            {
                wallMgr.CloseAutoBusiness();
            }
            
            // 结束营业的时候，移除夜总会内的英雄小人
            var area = GetArea(CityRealAiConst.WallAreaId);
            if (area == null)
            {
                return;
            }
            var roomMgr = area.GetRoom(CityRealAiConst.HallId, 1);
            if (roomMgr != null)
            {
                var npcCtrl = roomMgr.GetNpcCtrl(_heroRoleId, _heroUid);
                roomMgr.RemoveNpcCtrl(_heroRoleId, _heroUid);
                if (npcCtrl != null)
                {
                    npcCtrl.Dispose();
                }
            }
        }
        
        /// <summary>
        /// 触发大厅特殊事件
        /// </summary>
        /// <param name="specialEvendId">特殊事件id</param>
        /// <param name="totalCount">影响人数</param>
        /// <param name="additionValue">附加参数</param>
        public void TriggerHallSpecialEvent(int specialEvendId, int totalCount, int additionValue, bool killOtherBubble)
        {
            _hallSpecialEventNpcList.Clear();
            
            CityRealAiRoomManager roomMgr = GetCityRealAiHall();
            int count = 0;
            if (roomMgr != null)
            {
                _specialEvendId = specialEvendId;
                foreach (var dic in roomMgr.NpcTypeCtrlDic.Values)
                {
                    foreach (var npcCtrl in dic.Values)
                    {
                        if (npcCtrl.RoleClassType == (int)CityRealNpcClassType.Customer)
                        {
                            // 触发特殊事件
                            npcCtrl.TryTriggerSpecial(specialEvendId, additionValue, killOtherBubble);
                            // 记录npc
                            _hallSpecialEventNpcList.Add(npcCtrl);
                            
                            count++;
                        }

                        if (totalCount > 0 && count >= totalCount)
                        {
                            break;
                        }
                    }
                    
                    if (totalCount > 0 && count >= totalCount)
                    {
                        break;
                    }
                }
            }
        }
        
        /// <summary>
        /// 触发大厅恢复事件
        /// </summary>
        public void TriggerHallSpecialEventRecover()
        {
            foreach (var npcCtrl in _hallSpecialEventNpcList)
            {
                if (npcCtrl.IsAlive)
                {
                    // 触发特殊事件恢复事件
                    npcCtrl.TriggerSpecialRecover(_specialEvendId);
                }
            }
            _hallSpecialEventNpcList.Clear();
            _specialEvendId = 0;
        }

        public void TryTriggerSpecialRecover(int uid, int specialId)
        {
            CityRealAiRoomManager roomMgr = GetCityRealAiHall();
            if (roomMgr != null)
            {
                foreach (var dic in roomMgr.NpcTypeCtrlDic.Values)
                {
                    if (dic.TryGetValue(uid, out var npcCtrl))
                    {
                        // 触发特殊事件恢复事件
                        npcCtrl.TriggerSpecialRecover(specialId);
                        break;
                    }
                }
            }
        }
        
        public CityRealAiAreaManager GetArea(int areaId)
        {
            if (AreaDic!=null && AreaDic.TryGetValue(areaId, out var area))
            {
                return area;
            }

            return null;
        }

        public CityRealAiAreaManager GetOrCreateArea(int areaId)
        {
            CityRealAiAreaManager area = null;
            if (AreaDic!=null && !AreaDic.TryGetValue(areaId, out area))
            {
                area = new CityRealAiAreaManager(areaId);
                AreaDic.Add(areaId, area);
            }
            
            return area;
        }

        // 获取区域雇员位置
        public bool GetAreaStaffPosition(int staffId, out Vector3 pos)
        {
            if (StaffAreaDic.TryGetValue(staffId, out var npc))
            {
                if (npc.GetRoleObj() && npc.GetRoleObj().transform)
                {
                    pos = npc.GetRoleObj().transform.position;
                    return true;
                }
            }
            pos = Vector3.zero;
            return false;
        }

        //获取区域雇员对象
        public GameObject GetAreaStaffHUDById(int staffId)
        {
            if (StaffAreaDic.TryGetValue(staffId, out var npc))
            {
                if (HudMgr.StaffDic.TryGetValue(npc.Uid, out var hud))
                {
                    return hud.GetHudObj();
                }
            }
            return null;
        }

        public void RefreshAreaStaffHud(LuaTable table)
        {
            for (int i = 1; i <= table.Length; i++)
            {
                var npcInfoTb = table.Get<int, LuaTable>(i);
                var staffId = npcInfoTb.Get<string, int>("staffId");
                var hud = npcInfoTb.Get<string, string>("hud");
                if (StaffAreaDic.TryGetValue(staffId, out var npc))
                {
                    if (npc != null && npc.GetRoleObj() != null)
                    {
                        HudMgr.OnShowStaff(npc.Uid, CityRealAiConst.StaffBubble, npc.GetRoleObj().transform,
                            staffId, CityRealAiConst.DefaultBubbleBone);
                    }
                    else
                    {
                        Debug.LogError(LogModule.CitySimulation, $"staffId = {staffId}, npc = {npc}, roleObj = {npc?.GetRoleObj()}");
                    }
                }
            }
        }

        public void SetAreaStaffHudCollider(bool state)
        {
            foreach (var item in StaffAreaDic)
            {
                HudMgr.SetHudColliderState(item.Value.Uid, state);
            }
        }

        public void Pop(string path, Action<GameObject> callback, bool autoActive = true)
        {
            _PoolNames.Add(path);
            GameObjectPoolManager.GetInstance().Pop(path, callback, autoActive);
        }

        public void Push(string path, GameObject go)
        {
            GameObjectPoolManager.GetInstance().Push(path, go);
        }

        public void LoadResource(string path, Action<object> callback)
        {
            if (_resLoader == null)
            {
                _resLoader = ResLoader.Alloc();
            }
            _resLoader.Add2Load(CityRealAiConst.CityBusinessBlueprint,(success, name, asset) =>
            {
                if (success)
                {
                    callback(asset);
                }
            }).Load();
        }

        private void OnInterval(object param)
        {
            if (!_isInitialize)
            {
                return;
            }
            Update();
        }

        public override void Update()
        {
            foreach (var keyValuePair in AreaDic)
            {
                keyValuePair.Value.OnUpdate();
            }
            // 商品气泡状态
            UpdateCommodityBubbleStatus();
        }

        public override void Dispose()
        {
            Timers.inst.Remove(OnInterval);
            _needCreateRoomNPCNum = -1;
            
            if (AreaDic != null)
            {
                foreach (var keyValuePair in AreaDic)
                {
                    keyValuePair.Value.Dispose();
                }
                AreaDic.Clear();
                AreaDic = null;
            }

            if (StaffAreaDic != null)
            {
                foreach (var keyValuePair in StaffAreaDic)
                {
                    keyValuePair.Value.Dispose();
                }
                StaffAreaDic.Clear();
                StaffAreaDic = null;
            }

            if (ConfigMgr != null)
            {
                ConfigMgr.Dispose();
                ConfigMgr = null;
            }

            if (HudMgr != null)
            {
                HudMgr.OnDispose();
                HudMgr = null;
            }

            if (PoolMgr != null)
            {
                PoolMgr.OnDispose();
                PoolMgr = null;
            }

            if (OutCityPointInfo != null)
            {
                OutCityPointInfo.Reset();
                OutCityPointInfo = null;
            }

            if (InCityPointInfo != null)
            {
                InCityPointInfo.Reset();
                InCityPointInfo = null;
            }

            AstarPath.OnLatePostScan -= OnLatePostScan;
            if (_scanCompleteCallback != null)
            {
                _scanCompleteCallback.Clear();
                _scanCompleteCallback = null;
            }
            if (this._scanIEnumerator != null)
            {
                Timers.inst.StopCoroutine(this._scanIEnumerator);
                this._scanIEnumerator = null;
            }

            if (_PoolNames != null)
            {
                foreach (string path in _PoolNames)
                {
                    GameObjectPoolManager.GetInstance().Destroy(path);
                }
                _PoolNames.Clear();
            }

            if (_CityBusinessBlueprint)
            {
                GameObject.Destroy(_CityBusinessBlueprint);
                _CityBusinessBlueprint = null;
            }

            if (_resLoader != null)
            {
                _resLoader.Recycle2Cache();
                _resLoader = null;
            }

            NpcMoveObjTemplete = null;
            CityRealAiRootTran = null;
            _dailyData = null;
            _isInitialize = false;
        }
        
#if UNITY_EDITOR
        public void DisposeExternal()
        {
            p_instance = null;
        }
#endif

        private void InitializeTween()
        {
            // 初始化128个选中的tween，供内城小人使用
            int poolSize = 128;
            var tweeners = new TweenerCore<Quaternion, Vector3, QuaternionOptions>[poolSize];
            for (int i = 0; i < poolSize; i++)
            {
                var tweener = ShortcutExtensions.DORotate(null, Vector3.zero, 0);
                tweener.SetRecyclable(true);
                tweeners[i] = tweener;
            }

            for (int i = 0; i < poolSize; i++)
            {
                tweeners[i].Kill();
                tweeners[i] = null;
            }
        }

        public void RemoveStaffNpcByStaffId(LuaTable table)
        {
            for (int tableIndex = 1; tableIndex <= table.Length; tableIndex++)
            {
                var staffId = table.Get<int, int>(tableIndex);
                if (StaffAreaDic.ContainsKey(staffId))
                {
                    var staffNpc = StaffAreaDic[staffId];
                    if (staffNpc != null)
                    {
                        staffNpc.RemoveStaffNpc();
                    }
                    StaffAreaDic.Remove(staffId);
                }
            }
        }

        // 商品气泡触发限制CD
        private float _CommodityPerlimitCD = 5;
        // 商品气泡触发限制数量
        private int _CommodityPerlimitMaxCount = 2;
        // 商品气泡触发限制计时器
        private float _CommodityPerlimitTimer = 0;
        // 当前触发商品气泡数量
        private int _CommodityPerlimitCount = 0;


        public void SetCommodityPerlimitCD(float cd)
        {
            _CommodityPerlimitCD = cd;
        }
        public void SetCommodityPerlimitMaxCount(int count)
        {
            _CommodityPerlimitMaxCount = count;
            _CommodityPerlimitCount = 0;
        }
        public void SetCommodityPerlimitTimerAndCount(float timer, int count)
        {
            _CommodityPerlimitTimer = timer;
            _CommodityPerlimitCount = count;
        }

        // 刷新当前商品气泡状态
        public void UpdateCommodityBubbleStatus()
        {
            // 不在营业状态
            if (!CityRealAiConst.IsInOpenBusiness)
            {
                return;
            }

            // 大厅没有顾客
            bool hasCustomerInHall = false;
            CityRealAiRoomManager roomMgr = GetCityRealAiHall();
            if (roomMgr != null)
            {
                foreach (var dic in roomMgr.NpcTypeCtrlDic.Values)
                {
                    foreach (var npcCtrl in dic.Values)
                    {
                        if (npcCtrl.RoleClassType == (int)CityRealNpcClassType.Customer)
                        {
                            hasCustomerInHall = true;
                            break;
                        }
                    }
                }
            }
            if (!hasCustomerInHall)
            {
                return;
            }

            _CommodityPerlimitTimer += CityRealAiConst.UpdateDeltaTime;
            if (_CommodityPerlimitTimer >= _CommodityPerlimitCD)
            {
                _CommodityPerlimitTimer -= _CommodityPerlimitCD;
                _CommodityPerlimitCount = 0;
            }
        }
        
        public void AddCommodityBubbleCount()
        {
            _CommodityPerlimitCount++;
        }
        
        public bool CheckCommodityBubblePerlimit()
        {
            return _CommodityPerlimitCount < _CommodityPerlimitMaxCount;
        }
        
    }
}
